ExpiredStatusTab.onCheckboxChanged   F
last analyzed

Complexity

Conditions 17

Size

Total Lines 9
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 17
eloc 8
dl 0
loc 9
rs 1.8
c 0
b 0
f 0

How to fix   Complexity   

Complexity

Complex classes like ExpiredStatusTab.onCheckboxChanged often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
import React from 'react';
2
import PropTypes from 'prop-types';
3
import {
4
  ButtonToolbar,
5
  ButtonGroup,
6
  Button,
7
  Row,
8
  Col,
9
  Pagination,
10
} from 'react-bootstrap';
11
import moment from 'moment';
12
import BaseRefreshableTab from './BaseRefreshableTab';
13
import Loading from '../Loading';
14
import Table from '../Table';
15
import Modal from '../modal/Modal';
16
import StatusModal from '../modal/StatusModal';
17
import dateUtil from '../../common/date-util';
18
19
import Api from '../../common/api';
20
21
const ASTERISK = '✱';
22
const DATE_FORMAT = 'YYYY-MM-DD HH:mm';
23
24
export default class ExpiredStatusTab extends BaseRefreshableTab {
25
  constructor(props) {
26
    super(props);
27
28
    this.state = {
29
      items: [],
30
      checkedItems: [],
31
      page: 1,
32
      totalCount: 0,
33
      countPerPage: 10,
34
      buttonDisabled: {
35
        add: false,
36
        modify: true,
37
        remove: true,
38
        clone: true,
39
        activate: true,
40
      },
41
      error: null,
42
      modal: {
43
        visible: false,
44
        mode: 'confirm',
45
        title: '',
46
        onSuccess: () => {},
47
        onConfirm: () => {},
48
        onClose: () => this.setSubState('modal', { visible: false }),
49
        content: '',
50
        data: null,
51
      },
52
      showLoading: false,
53
    };
54
55
    this.options = {
56
      statusTypes: props.statusTypes ? props.statusTypes : [],
57
      deviceTypes: props.deviceTypes ? props.deviceTypes : [],
58
    };
59
60
    this.columns = [
61
      {
62
        title: 'ID',
63
        isChildRow: true,
64
        display: row => <span>{row.id}</span>,
65
      },
66
      {
67
        title: '상태 타입',
68
        display: (row) => {
69
          if (!row.type) { return ''; }
70
          let typeLabel = 'Unknown';
71
          for (let i = 0; i < this.options.statusTypes.length; i++) {
72
            if (row.type === this.options.statusTypes[i].value) {
73
              typeLabel = this.options.statusTypes[i].label;
74
              break;
75
            }
76
          }
77
          return <span>{typeLabel}</span>;
78
        },
79
      },
80
      { title: '제목', display: row => <span>{row.title}</span> },
81
      {
82
        title: '기간',
83
        display: (row) => {
84
          if (!row.startTime || !row.endTime) {
85
            return <span>{ASTERISK}</span>;
86
          }
87
          const start = moment(row.startTime);
88
          const end = moment(row.endTime);
89
          const duration = start.from(end, true);
90
          return <span>{`${dateUtil.formatDate(start, DATE_FORMAT)} ~ ${dateUtil.formatDate(end, DATE_FORMAT)} (${duration})`}</span>;
91
        },
92
      },
93
      {
94
        title: '디바이스 타입',
95
        display: (row) => {
96
          if (!row.deviceTypes) {
97
            return <span />;
98
          }
99
          if (row.deviceTypes.length === this.options.deviceTypes.length) {
100
            return <span>{ASTERISK}</span>;
101
          }
102
          return row.deviceTypes
103
            ? (
104
              <span>
105
                {row.deviceTypes.map((device) => {
106
                  let typeLabel = 'Unknown';
107
                  for (let i = 0; i < this.options.deviceTypes.length; i++) {
108
                    if (device === this.options.deviceTypes[i].value) {
109
                      typeLabel = this.options.deviceTypes[i].label;
110
                      break;
111
                    }
112
                  }
113
                  return typeLabel;
114
                }).join(', ')}
115
              </span>
116
            )
117
            : <span />;
118
        },
119
      },
120
      {
121
        title: '디바이스 버전',
122
        display: row => ((row.deviceSemVersion === '*') ? <span>{ASTERISK}</span> : row.deviceSemVersion),
123
      },
124
      {
125
        title: '앱 버전',
126
        display: row => ((row.appSemVersion === '*') ? <span>{ASTERISK}</span> : row.appSemVersion),
127
      },
128
      {
129
        title: '내용',
130
        isChildRow: true,
131
        display: row => (!row.contents ? '' : <span dangerouslySetInnerHTML={{ __html: row.contents }} />),
132
      },
133
    ];
134
  }
135
136
  onCheckboxChanged(checkedItems) {
137
    let buttonDisabled = { add: false, modify: true, remove: true, clone: true, activate: true };
138
    if (checkedItems.length === 1) {
139
      buttonDisabled = { add: false, modify: false, remove: false, clone: false, activate: false };
140
    } else if (checkedItems.length > 1) {
141
      buttonDisabled = { add: false, modify: true, remove: false, clone: true, activate: false };
142
    }
143
    this.setState({ buttonDisabled, checkedItems });
144
  }
145
146
  remove(items) {
147
    if (items instanceof Array && items.length > 0) {
148
      Promise.all(items.map(item => Api.removeStatus(item.id)))
149
        .then(() => {
150
          this.refresh();
151
          this.state.modal.onClose();
152
        })
153
        .catch(error => this.setState({ error }));
154
    }
155
  }
156
157
  refresh() {
158
    const {
159
      page,
160
      countPerPage,
161
    } = this.state;
162
163
    this.setState({ showLoading: true });
164
    const offset = (page - 1) * countPerPage;
165
    const limit = countPerPage;
166
    return Api.getStatus('expired', (page - 1) * offset, limit)
167
      .then((response) => {
168
        this.setState({
169
          items: response.data.data,
170
          totalCount: response.data.totalCount,
171
          error: null,
172
          checkedItems: [],
173
          showLoading: false,
174
        });
175
        this.table.setChecked(false);
176
      })
177
      .catch((error) => {
178
        this.setState({
179
          items: [],
180
          totalCount: 0,
181
          error,
182
          checkedItems: [],
183
          showLoading: false,
184
        });
185
        this.table.setChecked(false);
186
      });
187
  }
188
189
  onClickButton(modalName) {
190
    const { checkedItems } = this.state;
191
    switch (modalName) {
192
      case 'add':
193
        this.setSubState('modal', {
194
          visible: true,
195
          mode: 'add',
196
          onSuccess: () => this.refresh(),
197
          data: null,
198
        });
199
        break;
200
      case 'clone':
201
        this.setSubState('modal', {
202
          visible: true,
203
          mode: 'add',
204
          onSuccess: () => this.refresh(),
205
          data: checkedItems[0],
206
        });
207
        break;
208
      case 'modify':
209
        this.setSubState('modal', {
210
          visible: true,
211
          mode: 'modify',
212
          onSuccess: () => this.refresh(),
213
          data: checkedItems[0],
214
        });
215
        break;
216
      case 'remove':
217
        this.setSubState('modal', {
218
          visible: true,
219
          mode: 'confirm',
220
          title: '삭제 확인',
221
          onConfirm: () => this.remove(checkedItems),
222
          content: <p>{checkedItems.length} 건의 데이터를 삭제하시겠습니까?</p>,
223
        });
224
        break;
225
      default:
226
        break;
227
    }
228
  }
229
230
  renderModal() {
231
    const {
232
      modal,
233
    } = this.state;
234
235
    if (modal.mode === 'add' || modal.mode === 'modify') {
236
      return (
237
        <StatusModal
238
          visible={modal.visible}
239
          mode={modal.mode}
240
          options={this.options}
241
          data={modal.data}
242
          onSuccess={() => this.refresh()}
243
          onClose={() => modal.onClose()}
244
        />
245
      );
246
    }
247
    return (
248
      <Modal
249
        visible={modal.visible}
250
        mode={modal.mode}
251
        title={modal.title}
252
        onSuccess={() => modal.onSuccess()}
253
        onConfirm={() => modal.onConfirm()}
254
        onClose={() => modal.onClose()}
255
      >
256
        {modal.content}
257
      </Modal>
258
    );
259
  }
260
261
  render() {
262
    const {
263
      items,
264
      buttonDisabled,
265
      totalCount,
266
      page,
267
      countPerPage,
268
      error,
269
    } = this.state;
270
271
    return (
272
      <div>
273
        <ButtonToolbar>
274
          <ButtonGroup>
275
            <Button onClick={() => this.onClickButton('add')} bsSize="small" disabled={buttonDisabled.add}>등록</Button>
276
            <Button onClick={() => this.onClickButton('clone')} bsSize="small" disabled={buttonDisabled.clone}>복제</Button>
277
          </ButtonGroup>
278
          <Button onClick={() => this.onClickButton('modify')} bsSize="small" disabled={buttonDisabled.modify}>수정</Button>
279
          <Button onClick={() => this.onClickButton('remove')} bsSize="small" disabled={buttonDisabled.remove}>삭제</Button>
280
        </ButtonToolbar>
281
282
        <Row className="table-info">
283
          <Col xs={6} className="table-info-left">총 {totalCount || 0}건의 데이터</Col>
284
          <Col xs={6} className="table-info-right">{page} / {Math.ceil(totalCount / countPerPage) || 1} 페이지</Col>
285
        </Row>
286
287
        <Table
288
          ref={(t) => { this.table = t; }}
289
          items={items}
290
          columns={this.columns}
291
          error={error}
292
          onCheckboxChange={(newChechedItems => this.onCheckboxChanged(newChechedItems))}
293
          onPageChange={newPage => this.setState({ page: newPage }, () => this.refresh())}
294
          page={page}
295
          totalPage={Math.ceil(totalCount / countPerPage) || 1}
296
          showCheckbox
297
        />
298
        <div className="text-center">
299
          <Pagination
300
            prev
301
            next
302
            first
303
            last
304
            items={Math.ceil(totalCount / countPerPage) || 1}
305
            activePage={page || 1}
306
            onSelect={newPage => this.setState({ page: newPage }, () => this.refresh())}
307
          />
308
        </div>
309
        {this.renderModal()}
310
        <Loading show={this.state.showLoading} />
311
      </div>
312
    );
313
  }
314
}
315
316
ExpiredStatusTab.propTypes = {
317
  statusTypes: PropTypes.arrayOf(PropTypes.shape({
318
    label: PropTypes.string,
319
    value: PropTypes.string,
320
  })).isRequired,
321
  deviceTypes: PropTypes.arrayOf(PropTypes.shape({
322
    label: PropTypes.string,
323
    value: PropTypes.string,
324
  })).isRequired,
325
};
326